package isquirrel.semantic;

import isquirrel.beans.Property;
import isquirrel.beans.Recommendation;
import isquirrel.util.URL;

import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;

public class DBpedia extends SPARQLendpoint {

	// this are actually useful attributes, so I think we should forget about
	// the blacklist...
	// static private String blacklist[]
	// = {"http://dbpedia.org/property/relatedInstance",
	// "http://dbpedia.org/property/wikiPageUsesTemplate",
	// "http://www.w3.org/2002/07/owl#sameAs"
	// };

	private Vector<String> _restricted;

	public DBpedia() {
		super("", "http://dbpedia.org/sparql");

		_restricted = new Vector<String>();
		_restricted.add("http://dbpedia.org/property/wikilink");
		_restricted.add("http://dbpedia.org/property/wikiPageUsesTemplate");

	}

	DBpedia(String prefix, String endpoint) {
		super(prefix, endpoint);
	}

	// the subjects are skos:subjects
	public Set<String> getMoreSpecificSubjects(Set<String> subjects) {
		Set<String> specific = new HashSet<String>();

		for (String subject : subjects) {
			String query = "SELECT ?broader WHERE { ?broader"
					+ " <http://www.w3.org/2004/02/skos/core#broader> <"
					+ subject + "> }";

			QueryExecution qexec = null;
			ResultSet results = null;
			try {
				qexec = QueryExecutionFactory.sparqlService(this.endpoint,
						query);
				results = qexec.execSelect();

				while (results.hasNext()) {
					QuerySolution solution = results.nextSolution();
					Resource x = solution.getResource("broader");
					String dbPediaURL = x.toString();
					specific.add(dbPediaURL);
				}
			} catch (Exception ex) {
				System.err.println("EXCEPTION: in getMoreSpecificSubjects " + ex.getMessage());
			} finally {
				if (qexec != null)
					qexec.close();
			}
		}
		return specific;
	}
	
	public List<String> getMoreSpecificSubjectsList(Set<String> subjects) {
		List<String> specific = new LinkedList<String>();

		for (String subject : subjects) {
			String query = "SELECT ?broader WHERE { ?broader"
					+ " <http://www.w3.org/2004/02/skos/core#broader> <"
					+ subject + "> }";

			QueryExecution qexec = null;
			ResultSet results = null;
			try {
				qexec = QueryExecutionFactory.sparqlService(this.endpoint,
						query);
				results = qexec.execSelect();

				while (results.hasNext()) {
					QuerySolution solution = results.nextSolution();
					Resource x = solution.getResource("broader");
					String dbPediaURL = x.toString();
					specific.add(dbPediaURL);
				}
			} catch (Exception ex) {
				System.err.println("EXCEPTION: in getMoreSpecificSubjects " + ex.getMessage());
			} finally {
				if (qexec != null)
					qexec.close();
			}
		}
		return specific;
	}

	
	public Set<String> getMoreSpecificSubjects(String subject) {
		Set<String> specific = new HashSet<String>();

			String query = "SELECT ?broader WHERE { ?broader"
					+ " <http://www.w3.org/2004/02/skos/core#broader> <"
					+ subject + "> }";

			QueryExecution qexec = null;
			ResultSet results = null;
			try {
				qexec = QueryExecutionFactory.sparqlService(this.endpoint,
						query);
				results = qexec.execSelect();

				while (results.hasNext()) {
					QuerySolution solution = results.nextSolution();
					Resource x = solution.getResource("broader");
					String dbPediaURL = x.toString();
					specific.add(dbPediaURL);
				}
			} catch (Exception ex) {
				System.err.println("EXCEPTION: in getMoreSpecificSubjects " + ex.getMessage());
			} finally {
				if (qexec != null)
					qexec.close();
			}
		return specific;
	}
		
	@Override
	public List<Property> getPageInformation(String wikipediaURL) {
		String dbPediaPage = URL.convertWIKIpediaToDBpedia(wikipediaURL);
		String query = "SELECT ?redirect WHERE { <" + dbPediaPage
				+ "> <http://dbpedia.org/property/redirect> ?redirect }";
		QueryExecution qexec = null;
		ResultSet results = null;
		try {
			qexec = QueryExecutionFactory.sparqlService(this.endpoint, query);
			results = qexec.execSelect();

			if (results != null && results.hasNext()) {
				QuerySolution soln = results.nextSolution();
				RDFNode redirect = soln.get("redirect");
				if (redirect != null) {
					dbPediaPage = redirect.toString();
				}
			}
		} catch (Exception ex) {
			System.err.println("EXCEPTION: " + ex.getMessage());
		} finally {
			if (qexec != null)
				qexec.close();
		}

		query = "SELECT ?attribute ?value WHERE { <" + dbPediaPage
				+ "> ?attribute ?value }";
		List<Property> list = new LinkedList<Property>();

		qexec = null;
		results = null;
		try {
			qexec = QueryExecutionFactory.sparqlService(this.endpoint, query);
			results = qexec.execSelect();

			while (results.hasNext()) {
				QuerySolution soln = results.nextSolution();
				if (soln.get("value").isResource()) {
					RDFNode attribute = soln.get("attribute");
					Resource value = soln.getResource("value");
					if (isAllowed(attribute.toString()) && isValueAllowed(value.toString())) {
						list.add(new Property(attribute.toString(), value
								.toString()));
					}
				}
			}
		} catch (Exception ex) {
			System.err.println("EXCEPTION: in getPageInformation " + ex.getMessage());

		} finally {
			if (qexec != null)
				qexec.close();
		}
		return list;
	}

	private boolean isAllowed(String attr) {

		return !_restricted.contains(attr);

	}
	
	private boolean isValueAllowed(String value) {
		return !value.contains("http://sw.opencyc.org/");
	}

	public Set<String> getUrlsFromSubjects(Set<String> subjects) {
		Set<String> results = new HashSet<String>();

		for (String s : subjects) {
			List<Property> l = new LinkedList<Property>();
			l
					.add(new Property(
							"http://www.w3.org/2004/02/skos/core#subject", s));
			results.addAll(getUrls(l));
		}

		return results;
	}
	
	public List<String> getUrlsFromSubject(String subject) {
		List<String> results = new LinkedList<String>();
			List<Property> l = new LinkedList<Property>();
			l
					.add(new Property(
							"http://www.w3.org/2004/02/skos/core#subject", subject));
			results.addAll(getUrls(l));
		return results;
	}
	
	
	@Override
	public Set<String> getUrls(List<Property> properties) {
		if (properties == null || properties.size() == 0)
			return new HashSet<String>(); // return an empty list

		Set<String> result = new HashSet<String>();
		String queryString = "SELECT  DISTINCT ?x WHERE {";

		for (Iterator<Property> it = properties.iterator(); it.hasNext();) {
			Property property = it.next();
			queryString += " ?x <" + property.getAttribute() + "> <"
					+ property.getValue() + ">";
			if (it.hasNext())
				queryString += " .";
		}
		queryString += " }";

		QueryExecution qexec = null;
		ResultSet results = null;
		try {
			qexec = QueryExecutionFactory.sparqlService(this.endpoint,
					queryString);
			results = qexec.execSelect();

			for (; results.hasNext();) {
				QuerySolution solution = results.nextSolution();
				Resource x = solution.getResource("x");
				String dbPediaURL = x.toString();
				result.add(URL.convertDBpediaToWIKIpedia(dbPediaURL));
			}
		} catch (Exception ex) {
			System.err.println("EXCEPTION in getUrls: " + ex.getMessage());
		} finally {
			if (qexec != null)
				qexec.close();
		}
		return result;
	}

	public String getSomeProperty(String wikipediaURL, String attribute) {
		String query = "SELECT ?x WHERE { <"
				+ URL.convertWIKIpediaToDBpedia(wikipediaURL) + "> <"
				+ attribute + "> ?x }";

		QueryExecution qexec = null;
		ResultSet results = null;
		try {
			qexec = QueryExecutionFactory.sparqlService(this.endpoint, query);
			results = qexec.execSelect();

			if (results.hasNext()) {
				QuerySolution soln = results.nextSolution();
				RDFNode x = soln.get("x");
				return x.toString();
			} else
				return null;
		} catch (Exception ex) {
			System.err.println("EXCEPTION in getSomeProperty: " + ex.getMessage());

		} finally {
			if (qexec != null)
				qexec.close();
		}
		return null;
	}

	// subject is NOT a skos:subject, it's a human-readable one...
	public Set<String> getSubjects(String label) {
		String dbPediaPage = URL.constructDBpediaPage(label);
		return getSubjects(dbPediaPage, false);
	}
	
	public synchronized String getDescription(String subject)
	{		
		boolean isInterest = false;
		String dbPediaPage = "";
		if (subject.contains("http://") || subject.contains("https://")){
			dbPediaPage = URL.convertWIKIpediaToDBpedia(subject);
			
		}
		else{
			dbPediaPage = URL.constructDBpediaPage(subject);
			isInterest = true;
		}
		
		return _getDescription(dbPediaPage, false, isInterest);
	}
	
	private synchronized String _getDescription(String dbPediaPage, boolean comeFromRedirect, boolean isInterest) {
		String desc = "";
		String query = "SELECT ?redirect ?abstract WHERE { {<" + dbPediaPage
				+ "> <http://dbpedia.org/property/redirect> ?redirect }"
				+ " UNION {<" + dbPediaPage
				+ "> <http://dbpedia.org/property/abstract> ?abstract filter( lang(?abstract) = \"en\")}"
				+ " }";

		QueryExecution qexec = null;
		ResultSet queryResults = null;
		try {
			qexec = QueryExecutionFactory.sparqlService(this.endpoint, query);
			queryResults = qexec.execSelect();

			if (queryResults.hasNext()) {
				QuerySolution solution = queryResults.nextSolution();
				RDFNode redirect = solution.get("redirect");
				if (redirect != null && !comeFromRedirect) {
					return _getDescription(redirect.toString(), true, isInterest);
				}
				RDFNode a = solution.get("abstract");
				if( a != null ) {
					String text = a.toString();
					if ( text.length() > 3 && text.substring(text.length()-3).equals("@en")) {
						text = text.substring(0, text.length()-3);
					}
					if (isInterest){
						if ( text.length() >= 150){
							text = text.substring(0, 150) + "...";
						}
						desc = text;
					}
					else{
						String abstr = "";
						String[] words = text.split(" ");
						
						int i = 0;
						String end = "";
						for (String word: words){
							if (i == 75) {end = "...";break;}
							abstr += word + " ";
							if (abstr.length() > 450) {end = "..."; break; } 
							++i;
						}
						desc = abstr.trim() + end;
					}
				} else {
					desc = "No description found.";
				}
			} else
				return "No description found.";
		} catch (Exception ex) {
			System.err.println("EXCEPTION in getDescription(): " + ex.getMessage());

		} finally {
			if (qexec != null)
				qexec.close();
		}
		return desc;
	}
	
	// subject corresponds to the subject entered by the user
	private Set<String> getSubjects(String dbPediaPage, boolean comeFromRedirect) {
		Set<String> result = new HashSet<String>();
		String query = "SELECT ?redirect ?subject WHERE { {<" + dbPediaPage
				+ "> <http://dbpedia.org/property/redirect> ?redirect }"
				+ " UNION {<" + dbPediaPage
				+ "> <http://www.w3.org/2004/02/skos/core#subject> ?subject"
				+ " } }";

		QueryExecution qexec = null;
		ResultSet queryResults = null;
		try {
			qexec = QueryExecutionFactory.sparqlService(this.endpoint, query);
			queryResults = qexec.execSelect();

			if (queryResults.hasNext()) {
				QuerySolution solution = queryResults.nextSolution();
				RDFNode redirect = solution.get("redirect");
				if (redirect != null && !comeFromRedirect) {
					return getSubjects(redirect.toString(), true);
				}
				Resource subj = solution.getResource("subject");
				result.add(subj.toString());
				for (; queryResults.hasNext();) {
					solution = queryResults.nextSolution();
					subj = solution.getResource("subject");
					result.add(subj.toString());
				}
			} else
				return result;
		} catch (Exception ex) {
			System.err.println("EXCEPTION in getSubjects: " + ex.getMessage());

		} finally {
			if (qexec != null)
				qexec.close();
		}
		return result;
	}

	public String getHomepage(String wikipediaURL) {
		return getSomeProperty(wikipediaURL,
				"http://xmlns.com/foaf/0.1/homepage");
	}

	public String getThumbnail(String wikipediaURL) {
		return getSomeProperty(wikipediaURL,
				"http://dbpedia.org/ontology/thumbnail");
	}

	//input: r.url
	public void fillRecommendation(Recommendation r) {
		String dbPage = URL.convertWIKIpediaToDBpedia(r.getUrl());
		
		String query = "SELECT ?abstract ?homepage WHERE { { <"
					+ dbPage + "> <http://xmlns.com/foaf/0.1/homepage> ?homepage } UNION { <" 
					+ dbPage 
					+ "> <http://dbpedia.org/property/abstract> ?abstract filter( lang(?abstract) = \"en\")} }";

		QueryExecution qexec = null;
		ResultSet results = null;
		try {
			qexec = QueryExecutionFactory.sparqlService(this.endpoint, query);
			results = qexec.execSelect();

			while (results.hasNext()) {
				QuerySolution soln = results.nextSolution();
				
				RDFNode	a = soln.get("abstract");
				RDFNode w = soln.get("homepage");
				if ( w != null ) r.setWebsite(w.toString());

				if( a != null ) {
					String text = a.toString();
					if ( text.length() > 3 && text.substring(text.length()-3).equals("@en")) {
						text = text.substring(0, text.length()-3);
					}
					r.setAbstract(text);
				}
			} 
		} catch (Exception ex) {
			System.err.println("EXCEPTION in fillRecommendation: " + ex.getMessage());

		} finally {
			if (qexec != null)
				qexec.close();
		}
	}

}
